{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 载入数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#!wget http://dl4img-1251985129.cosbj.myqcloud.com/dogcat.tar.gz\n",
    "#!tar -zxvf dogcat.tar.gz"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# if coreml were not installed yet, go to terminal, then \n",
    "#!bash\n",
    "#!source activate py2\n",
    "#!pip install tensorflow-gpu==1.2.1 keras==2.0.6 coremltools==0.6.3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "from tqdm import tqdm\n",
    "\n",
    "n = 25000\n",
    "width = 224\n",
    "\n",
    "X = np.zeros((n, width, width, 3), dtype=np.uint8)\n",
    "y = np.zeros((n, 2), dtype=np.uint8)\n",
    "\n",
    "for i in tqdm(range(n/2)):\n",
    "    X[i] = cv2.resize(cv2.imread('train/cat.%d.jpg' % i), (width, width))\n",
    "    X[i+n/2] = cv2.resize(cv2.imread('train/dog.%d.jpg' % i), (width, width))\n",
    "\n",
    "y[:n/2, 0] = 1\n",
    "y[n/2:, 1] = 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 提取特征"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from keras.layers import *\n",
    "from keras.models import *\n",
    "from keras.applications import *\n",
    "from keras.optimizers import *\n",
    "from keras.regularizers import *\n",
    "from keras.applications.resnet50 import preprocess_input"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def preprocess_input(x):\n",
    "    return x - [103.939, 116.779, 123.68]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def get_features(MODEL, data=X):\n",
    "    cnn_model = MODEL(include_top=False, input_shape=(width, width, 3), weights='imagenet')\n",
    "    \n",
    "    inputs = Input((width, width, 3))\n",
    "    x = inputs\n",
    "    x = Lambda(preprocess_input, name='preprocessing')(x)\n",
    "    x = cnn_model(x)\n",
    "    x = GlobalAveragePooling2D()(x)\n",
    "    cnn_model = Model(inputs, x)\n",
    "\n",
    "    features = cnn_model.predict(data, batch_size=64, verbose=1)\n",
    "    return features"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "features = get_features(ResNet50, X)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "inputs = Input(features.shape[1:])\n",
    "x = inputs\n",
    "x = Dropout(0.5)(x)\n",
    "x = Dense(2, activation='softmax', kernel_regularizer=l2(1e-4), bias_regularizer=l2(1e-4))(x)\n",
    "model = Model(inputs, x, name='prediction')\n",
    "model.compile(optimizer='adam',\n",
    "              loss='binary_crossentropy',\n",
    "              metrics=['accuracy'])\n",
    "h = model.fit(features, y, batch_size=128, epochs=10, validation_split=0.2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "model.save('model_dense.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from IPython.display import SVG\n",
    "from keras.utils.vis_utils import model_to_dot\n",
    "\n",
    "SVG(model_to_dot(model, show_shapes=True).create(prog='dot', format='svg'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "weights = model.get_weights()[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "cnn_model = ResNet50(include_top=False, input_shape=(width, width, 3), weights='imagenet')\n",
    "cnn_model = Model(cnn_model.input, cnn_model.layers[-2].output, name='resnet50')\n",
    "\n",
    "inputs = Input((width, width, 3))\n",
    "x = inputs\n",
    "x = cnn_model(x)\n",
    "cam = Conv2D(2, 1, use_bias=False, name='cam')(x)\n",
    "model_cam = Model(inputs, cam)\n",
    "\n",
    "x = GlobalAveragePooling2D(name='gap')(x)\n",
    "x = model(x)\n",
    "model_clf = Model(inputs, x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "model_cam.layers[-1].set_weights([weights.reshape((1, 1, 2048, 2))])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "SVG(model_to_dot(model_cam, show_shapes=True).create(prog='dot', format='svg'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "SVG(model_to_dot(model_clf, show_shapes=True).create(prog='dot', format='svg'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import random\n",
    "%matplotlib inline\n",
    "%config InlineBackend.figure_format = 'retina'\n",
    "\n",
    "# 用模型进行预测\n",
    "index = 13734\n",
    "img = X[index]\n",
    "prediction = model_clf.predict(np.expand_dims(img, 0))\n",
    "prediction = prediction[0, 0]\n",
    "\n",
    "cam = model_cam.predict(np.expand_dims(img, 0))\n",
    "cam = cam[0, :, :, 1 if prediction < 0.5 else 0]\n",
    "\n",
    "# 调整 CAM 的范围\n",
    "cam /= 10\n",
    "cam[cam < 0] = 0\n",
    "cam[cam > 1] = 1\n",
    "cam = cv2.resize(cam, (224, 224))\n",
    "cam = np.uint8(255*cam)\n",
    "\n",
    "# 染成彩色\n",
    "heatmap = cv2.applyColorMap(cam, cv2.COLORMAP_JET)\n",
    "\n",
    "# 加在原图上\n",
    "out = cv2.addWeighted(img, 0.8, heatmap, 0.4, 0)\n",
    "\n",
    "# 显示图片\n",
    "plt.axis('off')\n",
    "plt.imshow(out[:,:,::-1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "model_clf.save('model_clf.h5')\n",
    "model_cam.save('model_cam.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from coremltools.converters.keras import convert"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from keras.models import *\n",
    "from IPython.display import SVG\n",
    "from keras.utils.vis_utils import model_to_dot\n",
    "\n",
    "model_clf = load_model('model_clf.h5')\n",
    "SVG(model_to_dot(model_clf, show_shapes=True).create(prog='dot', format='svg'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true,
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "coreml_model = convert('model_clf.h5', blue_bias=103.939, green_bias=116.779, red_bias=123.68, \n",
    "                       input_names=['image'], image_input_names='image', output_names='prediction')\n",
    "\n",
    "coreml_model.author = 'YPW'\n",
    "coreml_model.short_description = 'Dogs vs Cats'\n",
    "coreml_model.license = 'MIT'\n",
    "coreml_model.input_description['image'] = 'A 224x224 Image.'\n",
    "coreml_model.output_description['prediction'] = 'The probability of Dog and Cat.'\n",
    "coreml_model.save('model_clf.mlmodel')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true,
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "coreml_model = convert('model_cam.h5', blue_bias=103.939, green_bias=116.779, red_bias=123.68, \n",
    "                       input_names=['image'], image_input_names='image', output_names='cam')\n",
    "\n",
    "coreml_model.author = 'YPW'\n",
    "coreml_model.short_description = 'Dogs vs Cats'\n",
    "coreml_model.license = 'MIT'\n",
    "coreml_model.input_description['image'] = 'A 224x224 Image.'\n",
    "coreml_model.output_description['cam'] = 'The cam Image.'\n",
    "coreml_model.save('model_cam.mlmodel')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
